package raft

import (
	"sync"
	"time"
)

//leader选举管理


//
// example RequestVote RPC arguments structure.
// field names must start with capital letters!
//
//投票请求参数
type RequestVoteArgs struct {
	// Your data here (2A, 2B).
	//候选任期号
	Term int
	//候选人的ID
	Candiddateid int
	//候选人的最后日志索引
	LasLogindex int
	//候选人的日志任期号
	LasLogTerm int
}

//
// example RequestVote RPC reply structure.
// field names must start with capital letters!
//
type RequestVoteReply struct {
	// Your data here (2A).
	CurrenTerm int //当前任期号
	VoteCranted bool //是否获取到了结点的投票
}

//
// example RequestVote RPC handler.
//
//接受投票请求之后进行处理
func (rf *Raft) RequestVote(args *RequestVoteArgs, reply *RequestVoteReply) {
	// Your code here (2A, 2B).
	//对整个函数进行加锁
	rf.mu.Lock()
	defer rf.mu.Unlock()
	//当前节点最后一个日志包含的任期号
	lastLogIndex := len(rf.Los) - 1
	//当前结点最后一个日志包含的任期号
	lastLogTrem := rf.Los[lastLogIndex].Term
    //判断发起选举节点的Trem是否比自己大
	if args.Term < rf.CurrentTerm{
		reply.CurrenTerm = rf.CurrentTerm
		reply.VoteCranted = false
	}else  {
           if args.Term > rf.CurrentTerm {
			   /**
			     将当前结点改为follower
			      根据Raft强一致性的特点,在一个任期的开始
			      只要没有第二状态你的状态必须为follower
			   */
			   rf.CurrentTerm = args.Term
			   rf.isLeader = false
			   rf.VotedFor = -1
			   //TODO 论文如果满足投票候选人的日志至少和自己一样新才能投票给他
			   if rf.VotedFor == -1 || rf.VotedFor == args.Candiddateid{
                     if (args.LasLogTerm == lastLogTrem &&
                     	args.LasLogindex >= lastLogIndex ||
                     	args.LasLogTerm > lastLogTrem){
                        rf.resetTimer <- struct{}{}
                        rf.isLeader = false
                        rf.VotedFor = args.Candiddateid
                        //满足以上条件投票
                        reply.VoteCranted = true
					 }
			   }
           }

	}
}

//
// example code to send a RequestVote RPC to a server.
// server is the index of the target server in rf.peers[].
// expects RPC arguments in args.
// fills in *reply with RPC reply, so caller should
// pass &reply.
// the types of the args and reply passed to Call() must be
// the same as the types of the arguments declared in the
// handler function (including whether they are pointers).
//
// The labrpc package simulates a lossy network, in which servers
// may be unreachable, and in which requests and replies may be lost.
// Call() sends a request and waits for a reply. If a reply arrives
// within a timeout interval, Call() returns true; otherwise
// Call() returns false. Thus Call() may not return for a while.
// A false return can be caused by a dead server, a live server that
// can't be reached, a lost request, or a lost reply.
//
// Call() is guaranteed to return (perhaps after a delay) *except* if the
// handler function on the server side does not return.  Thus there
// is no need to implement your own timeouts around Call().
//
// look at the comments in ../labrpc/labrpc.go for more details.
//
// if you're having trouble getting RPC to work, check that you've
// capitalized all field names in structs passed over RPC, and
// that the caller passes the address of the reply struct with &, not
// the struct itself.
//
func (rf *Raft) sendRequestVote(server int, args *RequestVoteArgs, reply *RequestVoteReply) bool {
	ok := rf.peers[server].Call("Raft.RequestVote", args, reply)
	return ok
}

//启动选举进程
func (rf *Raft)electionDaemo()  {
	for {
		select {
		//接收到重置请求之后的处理
		case <-rf.resetTimer:
			if !rf.electionTime.Stop(){
				//发送超时
				<-rf.electionTime.C
			}
			//重置选举
			rf.electionTime.Reset(rf.electionTimeout)
		case <- rf.electionTime.C:
			   /**
			     超时也就是follower在指定时间没有接收到
			     leader的信息,就自己变差candidate
			    */
			   //向其他结点发送投票信息
               go rf.canvassVotes()
			   //重置选举超时
			rf.electionTime.Reset(rf.electionTimeout)
	   }
	}
}

//填充请求参数
func (rf *Raft)fillReuestArgs(args *RequestVoteArgs)  {
	//修改状态是必须要加锁
	rf.mu.Lock()
	defer  rf.mu.Unlock()

	//任期号上+1
	rf.CurrentTerm += 1
	//由于是当前节点发起的请求所以,投票给自己
	rf.VotedFor = rf.me

	args.Term = rf.CurrentTerm
	args.Candiddateid = rf.me
	args.LasLogindex = len(rf.Los) - 1
	args.LasLogTerm = rf.Los[args.LasLogindex].Term
}

//发起选举请求
func (rf *Raft) canvassVotes() {
    	//投票请求参数
       var args RequestVoteArgs
       rf.fillReuestArgs(&args)

       /**
       正式发送请求,这里需要发送给除我自己以外的所有节点
        */
        //获取所有节点
       peers :=  len(rf.peers)
        //设置缓存chan  大小为服务器的所有节点数;保存结果，防止协程泄露
        resChan := make(chan RequestVoteReply,peers)
        var wg sync.WaitGroup
       for i :=0; i < peers ; i++  {
           if i == rf.me{
           	 //为了防止自己再次变成候选人
           	 rf.resetTimer <- struct{}{}
		   }else {
		     	wg.Add(1)
		   	  //不是自己发起投票
		   	  go func(n int) {
		   	  	defer  wg.Done()
		   	  	var res RequestVoteReply
                donech := make(chan bool,1)
		   	  	go func() {
		   	  		ok := rf.sendRequestVote(n,&args,&res)
		   	  		//将请求结果放到donech
		   	  		donech <- ok

				}()

				  select {
				  case ok := <- donech:
				  	//说明某个节点没有投票,返回
					  if !ok {
						  return
					  }
					  //将请求结果放入结果缓存通道
					  resChan <- res
				}
			  }(i)
		   }
       	}
       	//关闭结果通道
       	go func() {
       		wg.Wait()
       		close(resChan)
		}()
       //遍历缓存通道,统一每一个响应中的投票结果
       //等于1是因为自己会给自己投一票
       var votes int = 1
       for res := range resChan{
       	  if res.VoteCranted == true {
       	  	//如果投票数大于所有节点的一半当选成功
       	  	if votes ++;votes > peers /2 {
       	  		rf.mu.Lock()
				//改变节点状态为leader
       	  		rf.isLeader = true
       	  		rf.mu.Unlock()
       	  		//根据论文,一轮选举结束后,必须重置状态
       	  		rf.resElection()
       	  		//为了人别的节点知道我们的存在发起心跳机制,防止其他追随者变成候选人
				go rf.hearbeatDemo()
       	  		return
			}else if res.CurrenTerm > args.Term{
				/**
				根据论文,只有你的任期比我大在给你投票,
				 如果没有则拒绝投票,并我的任期号返回给你
				 */
				//改变状态,重新回到follow状态
				rf.mu.Lock()
				rf.isLeader = false
				rf.VotedFor = -1
				rf.CurrentTerm = res.CurrenTerm
				rf.mu.Unlock()
				rf.resetTimer <- struct{}{}
				return
			}
		  }
	   }
       //统计所有得到的票数.决定是否能够当选为leader
}


//启动心跳进程
func (rf *Raft)hearbeatDemo()  {
	 for{
	 	if _,isLeader := rf.GetState();isLeader{
			rf.resetTimer <- struct{}{}
		 }else {
		 	break
		}
		//设置心跳发送延时
		time.Sleep(rf.hearBEATInterval)
	 }
}
//选举成功重置相关状态
func(rf *Raft)resElection()  {
	  rf.mu.Lock()
	  defer rf.mu.Unlock()

	  cont := len(rf.peers)

	  lenth := len(rf.Los)

	for i:=0;i<cont ;i++  {
		rf.nextIndex[i] = lenth
		rf.matchIndex[i] = 0
		if i == rf.me{
			rf.matchIndex[i] = lenth -1
		}
	}
}